import fs from 'fs';
import path from 'path';
import DateUtils from '../utils/DateUtils.mjs';
import DltDataModel from './DltDataModel.mjs';
import DltOrderModel from './DltOrderModel.mjs';
import DltTplModel from './DltTplModel.mjs';
import FsDriver from './FsDriver.mjs';

const fsDriver = new FsDriver();
const dltTplModel = new DltTplModel();
const dltDataModel = new DltDataModel();
const dltOrderModel = new DltOrderModel();

/**
 * 定时任务控制器
 * @version 1.0.0
 */
class TaskModel {


    logFile = '';
    orderDirPath = '';
    ticketQuantity = 10;

    constructor() {
        this.orderDirPath = path.join(import.meta.dirname, '../data/dlt/');
        fsDriver.mkdir(this.orderDirPath);
        this.logFile = path.join(import.meta.dirname, '../data/log/task-' + (new DateUtils()).format('Ymd') + '.log');
        fsDriver.mkdir(path.dirname(this.logFile));
    }

    /**
     * 定时任务
     * 1. 创建基于50期开奖历史记录的所有订单，如果订单不存在则批量创建。
     * 2. 自动订单开奖
     * 3. 移动订单到 php-server。
     * @returns 
     */
    async run() {
        fsDriver.log(this.logFile, '任务开始');
        const dataset = [...dltDataModel.getDataSource()];
        if (dataset.length == 0) {
            fsDriver.log(this.logFile, '没有官方数据源，请先采集。')
            return;
        }
        // 检测并创建缺失的订单并开奖
        let n = 0;
        dataset.slice(0, -50).forEach((v, i) => {
            if (!fs.existsSync(this.orderDirPath + 'order-' + v.lotteryDrawNum + '.json')) {
                this.createFile(v.lotteryDrawNum);
                this.drawFile(v.lotteryDrawNum, v.lotteryDrawResult, v.lotteryDrawTime);
                n++;
            }
        })
        // 创建下期订单
        this.createFile('');
        fsDriver.log(this.logFile, '检测并创建缺失的订单并开奖 ' + n);
        // 移动订单文件
        let fromDir = path.join(import.meta.dirname, '../data/dlt/');
        let toDir = path.join(import.meta.dirname, '../../php-server/data/dlt/');
        fsDriver.copyDir(fromDir, toDir, {
            force: true,
            filter: src => fs.statSync(src).isDirectory() || (fs.statSync(src).isFile() && path.basename(src).startsWith('order-'))
        });
        fsDriver.log(this.logFile, '同步订单文件到 php-server');
        fsDriver.log(this.logFile, '任务结束');
    }

    createFile(activeDrawNum) {
        let dataset = dltDataModel.getDataSource(activeDrawNum);
        let statDrawal = dltDataModel.getDrawalStat(dataset);
        let prevDrawInfo = dataset[0];
        let activeDrawInfo = { lotteryDrawNum: parseInt(prevDrawInfo.lotteryDrawNum) + 1, lotteryDrawResult: '' };
        console.log('上期', prevDrawInfo, '本期', activeDrawInfo, '历史', dataset.length, dataset.map(v => v.lotteryDrawNum));
        let filename = this.orderDirPath + 'order-' + activeDrawInfo.lotteryDrawNum + '.json';
        if (fs.existsSync(filename)) {
            fsDriver.log(this.logFile, '创建订单文件失败，订单文件已存在：' + activeDrawInfo.lotteryDrawNum);
        } else {
            fsDriver.save(filename, JSON.stringify(this.batchGenerateOrders(statDrawal, activeDrawInfo, prevDrawInfo)));
            fsDriver.log(this.logFile, '创建订单文件成功：' + activeDrawInfo.lotteryDrawNum)
        }
        dataset = null;
    }

    batchGenerateOrders(statDrawal, activeDrawInfo, prevDrawInfo) {
        let ticketQuantity = this.ticketQuantity;
        let orderList = [];
        dltTplModel.getAllTplParams().forEach((param, i) => {
            orderList.push({
                ...dltTplModel.getTplForOrderListItem({
                    id: i + 1,
                    title: dltTplModel.getParamText(param),
                    ticketQuantity,
                    param
                })
            });
        });
        let n = orderList.length,
            drawResult = activeDrawInfo?.lotteryDrawResult?.trim(),
            prevDrawResult = prevDrawInfo?.lotteryDrawResult?.trim();
        let isDraw = drawResult?.length > 0;
        let nums = [], filterLevel = '', poolType = '', i = 10;
        for (let orderIdx = 0; orderIdx < n; orderIdx++) {
            orderList[orderIdx].list = [];
            orderList[orderIdx].drawList = [];
            filterLevel = parseInt(orderList[orderIdx].param.filterLevel);
            poolType = orderList[orderIdx].param.poolType;

            // 初选
            for (let i = 0; i < ticketQuantity; i++) {
                nums = dltOrderModel.getRandomNums7(statDrawal, poolType, filterLevel, prevDrawResult);
                orderList[orderIdx].list.push(nums[0].join(' ') + ' ' + nums[1].join(' '));
            }

            // 优选
            if (orderList[orderIdx].param.chooseMode == '优选') {
                orderList[orderIdx].list = dltOrderModel.getBetterNums7(statDrawal, orderList[orderIdx].list)
            }

            if (isDraw) {
                // 开奖
                orderList[orderIdx].drawList = dltOrderModel.calcOrder(drawResult, orderList[orderIdx].list, false, prevDrawResult);
                // 统计
                orderList[orderIdx].totalIncome = orderList[orderIdx].drawList.map(v => v.money)
                    .reduce((a, b) => {
                        return parseInt(a) + parseInt(b)
                    });
                orderList[orderIdx].netProfitMoney = orderList[orderIdx].totalIncome - (ticketQuantity * 2);
                orderList[orderIdx].maxPrize = dltOrderModel.getMaxPrize(orderList[orderIdx].drawList);
            }
        }
        return dltTplModel.getTplForFileData({
            PrevDrawalInfo: prevDrawInfo,
            DrawalInfo: {
                lotteryDrawNum: activeDrawInfo.lotteryDrawNum,
                lotteryDrawResult: "",
                lotteryDrawTime: ""
            },
            OrderList: orderList
        });
    }

    // 订单文件开奖
    drawFile(lotteryDrawNum, lotteryDrawResult, lotteryDrawTime) {
        if (!lotteryDrawResult || !lotteryDrawNum) {
            fsDriver.log(this.logFile, '订单文件开奖失败，本期尚未开奖：' + lotteryDrawNum + ' / ' + lotteryDrawResult);
            return;
        }
        let filename = this.orderDirPath + 'order-' + lotteryDrawNum + '.json';
        if (!fs.existsSync(filename)) {
            fsDriver.log(this.logFile, '订单文件开奖失败，文件不存在：' + lotteryDrawNum);
            return;
        }
        let fileData = JSON.parse(fsDriver.get(filename).toString());
        if (fileData.DrawalInfo.lotteryDrawResult) {
            fsDriver.log(this.logFile, '订单文件已经开奖过了，无需重复开奖：' + lotteryDrawNum);
            return;
        }
        fileData.DrawalInfo.lotteryDrawResult = lotteryDrawResult;
        fileData.DrawalInfo.lotteryDrawTime = lotteryDrawTime;
        let ticketQuantity = this.ticketQuantity;
        let orderList = fileData.OrderList;
        let n = orderList.length,
            prevDrawResult = fileData.PrevDrawalInfo?.lotteryDrawResult?.trim();
        let winBatchList = [];
        for (let orderIdx = 0; orderIdx < n; orderIdx++) {
            // 开奖
            orderList[orderIdx].drawList = dltOrderModel.calcOrder(lotteryDrawResult, orderList[orderIdx].list, true, prevDrawResult);
            // 统计
            orderList[orderIdx].totalIncome = 0;
            if (orderList[orderIdx].drawList.length > 0) {
                orderList[orderIdx].totalIncome = orderList[orderIdx].drawList.map(v => v.money)
                    .reduce((a, b) => {
                        return parseInt(a) + parseInt(b)
                    });
                orderList[orderIdx].maxPrize = dltOrderModel.getMaxPrize(orderList[orderIdx].drawList);
            }
            orderList[orderIdx].totalExpenses = ticketQuantity * 2;
            orderList[orderIdx].netProfitMoney = orderList[orderIdx].totalIncome - orderList[orderIdx].totalExpenses;
            // 中奖数据
            if (orderList[orderIdx].drawList.length > 0) {
                winBatchList.push({
                    id: orderList[orderIdx].id,
                    title: orderList[orderIdx].title,
                    drawList: orderList[orderIdx].drawList,
                    maxPrize: orderList[orderIdx].maxPrize,
                    totalIncome: orderList[orderIdx].totalIncome,
                    totalExpenses: orderList[orderIdx].totalExpenses,
                    netProfitMoney: orderList[orderIdx].netProfitMoney,
                });
            }
        }
        fileData.OrderList = orderList;
        fileData.StatInfo = this.statDrawOrder(orderList.length, ticketQuantity, winBatchList);
        winBatchList = null;
        fsDriver.save(filename, JSON.stringify(fileData));
        fsDriver.log(this.logFile, '订单开奖成功：' + lotteryDrawNum);
    }

    // 中奖汇总统计
    statDrawOrder(batchQuantity, ticketQuantity, winBatchList) {
        // 中奖汇总统计
        let statInfo = JSON.parse(JSON.stringify(dltTplModel.getTplForStatInfo()));
        statInfo.batchTicketCount = ticketQuantity; // 每批次投注数量
        statInfo.totalBatchCount = batchQuantity; // 总批次数量
        statInfo.totalTicketCount = batchQuantity * ticketQuantity; // 总注数
        statInfo.totalExpenses = statInfo.totalTicketCount * 2; // 总支出金额
        statInfo.winBatchCount = winBatchList.length; // 有中奖批次量
        statInfo.noWinBatchCount = statInfo.totalBatchCount - winBatchList.length; // 无中奖批次量
        statInfo.winBatchCountRate = Math.round(100 * winBatchList.length / statInfo
            .totalBatchCount); // 有中奖批次量比例
        statInfo.noWinBatchCountRate = 100 - statInfo.winBatchCountRate; // 无中奖批次量比例
        let k = '',
            maxPrizeValue = 10;
        winBatchList.forEach(item => {
            if (item.netProfitMoney > 0) {
                statInfo.profitBatchCount++; // 盈利批次量
            }
            statInfo.totalIncome += item.totalIncome; // 总收入金额
            if (item.maxPrize.value < maxPrizeValue) {
                statInfo.maxPrize = {
                    ...item.maxPrize
                };
                maxPrizeValue = item.maxPrize.value;
            }
            k = 'prize' + item.maxPrize.value;
            statInfo[k].list.push(item);
            statInfo[k].totalBatchCount++;
            statInfo[k].totalTicketCount += item.drawList.filter(v => v.status == item.maxPrize.value)
                .length;
            statInfo[k].totalIncome += item.totalIncome;
        })
        statInfo.lossBatchCount = statInfo.totalBatchCount - statInfo.profitBatchCount; // 亏损批次量
        statInfo.profitBatchCountRate = Math.round(100 * statInfo.profitBatchCount / statInfo
            .totalBatchCount); // 盈利批次量比例
        statInfo.lossBatchCountRate = 100 - statInfo.profitBatchCountRate; // 亏损批次量比例
        statInfo.netProfitMoney = statInfo.totalIncome - statInfo.totalExpenses; // 净收入金额
        return statInfo;
    }
}

export default TaskModel;